import clang.cindex
import os
import re
import argparse
import json

from utils import get_code_snippet, get_code_lines, is_system_function_declaration, LLVM_LIBRARY_PATH, clang_options, generate_header_path

def is_function_definition(cursor):
    """
    判断一个 FUNCTION_DECL 节点是否是函数定义。
    通过检查是否存在 COMPOUND_STMT（函数体）来判断。
    """
    for child in cursor.get_children():
        if child.kind == clang.cindex.CursorKind.COMPOUND_STMT:
            return True
    return False

def extract_definitions_from_header(header_path, include_dirs):
    if not clang.cindex.Config.loaded:
        # 配置libclang的路径，请根据你的实际路径进行配置
        clang.cindex.Config.set_library_path(LLVM_LIBRARY_PATH)
    index = clang.cindex.Index.create()
    translation_unit = index.parse(header_path, args=generate_header_path(include_dirs), options=clang_options)
    with open(header_path, "r") as h_file:
        header_full_code = h_file.readlines()
    definitions = list()
    processed_structs = set()  # 用于跟踪已处理的结构体名称
    processed_unions = set()  # 用于跟踪已处理的联合名称
    processed_enums = set()  # 用于跟踪已处理的枚举名称
    processed_externs = set() # tracing processed extern
    processed_typedefs = set()  # 用于跟踪已处理的typedef名称

    # 提取包含的头文件信息
    with open(header_path, 'r') as file:
        header_code = file.read()
        header_code = header_full_code
        parsed_filename = header_path.split(os.path.sep)[-1]
    for cursor in translation_unit.cursor.get_children():
        if is_system_function_declaration(cursor):
            continue
        cursor_filename = cursor.location.file.name if cursor.location.file.name else "empty filename!"
        if cursor.kind == clang.cindex.CursorKind.MACRO_DEFINITION:
            tokens = list(cursor.get_tokens())
            if len(tokens) < 2:
                continue
            elif len(tokens) == 2:
                definitions.append([f"#define {tokens[0].spelling} {tokens[1].spelling}", cursor_filename])
            else:
                include_header_code = header_code
                if parsed_filename not in cursor.extent.start.file.name:
                    include_header_code = get_code_lines(cursor.extent.start.file.name)
                value = ' '.join(token.spelling for token in tokens)
                definitions.append([f"#define {get_code_snippet(include_header_code, cursor)}", cursor_filename])
        elif cursor.kind == clang.cindex.CursorKind.INCLUSION_DIRECTIVE:
            tokens = list(cursor.get_tokens())
            system_header = False
            for token in tokens:
                if token.spelling == "<":
                    system_header = True
                    break
            include_header_code = header_code
            if parsed_filename not in cursor.extent.start.file.name:
                include_header_code = get_code_lines(cursor.extent.start.file.name)
            if system_header:
                definitions.append([get_code_snippet(include_header_code, cursor), cursor_filename])
        elif cursor.kind == clang.cindex.CursorKind.FUNCTION_DECL:
            #and \
            #    not cursor.extent.start.file.name.endswith(".c"):
            param_list = ", ".join([f"{p.type.spelling} {p.spelling}" for p in cursor.get_arguments()])
            if not cursor.extent.start.file.name.endswith(".h"):
                function_decl_content = f"{cursor.result_type.spelling} {cursor.spelling}({param_list});" 
            else:
                if not is_function_definition(cursor):
                    function_decl_content = f"{cursor.result_type.spelling} {cursor.spelling}({param_list});"
                else:
                    full_code = header_full_code
                    if parsed_filename not in cursor.extent.start.file.name:
                        full_code = get_code_lines(cursor.extent.start.file.name)
                    function_decl_content = get_code_snippet(full_code, cursor)

            if function_decl_content not in definitions:
                definitions.append([function_decl_content, cursor_filename])
        elif cursor.kind == clang.cindex.CursorKind.STRUCT_DECL:
            struct_name = cursor.spelling
            if not struct_name:
                continue
            struct_decl = f"struct {cursor.spelling} {{\n"
            struct_members = ""
            for field in cursor.get_children():
                if field.kind == clang.cindex.CursorKind.FIELD_DECL:
                    struct_members += f"    {field.type.spelling} {field.spelling};\n"
            if struct_members:
                struct_decl += struct_members
                struct_decl += "};"
                if struct_decl not in definitions:
                    definitions.append([struct_decl, cursor_filename])
                    processed_structs.add(struct_name)
        # Check if the node is an `extern` global variable declaration
        elif cursor.kind == clang.cindex.CursorKind.VAR_DECL and \
                (cursor.linkage == clang.cindex.LinkageKind.EXTERNAL or \
                cursor.linkage == clang.cindex.LinkageKind.INTERNAL):
            # Construct the extern variable declaration string
            full_code = header_full_code
            if parsed_filename not in cursor.extent.start.file.name:
                full_code = get_code_lines(cursor.extent.start.file.name)
            var_decl_content = get_code_snippet(full_code, cursor)

            if var_decl_content not in definitions:
                definitions.append([var_decl_content, cursor_filename])
                processed_externs.add(var_decl_content)
            
        elif cursor.kind == clang.cindex.CursorKind.TYPEDEF_DECL:
            typedef_name = cursor.spelling
            if typedef_name not in processed_typedefs:
                if len(list(cursor.get_children())) == 0:  # 基本数据类型的typedef
                    typedef_decl = f"typedef {cursor.underlying_typedef_type.spelling} {typedef_name};"
                    if typedef_decl not in definitions:
                        definitions.append([typedef_decl, cursor_filename])
                else:   # 复杂数据类型的typedef
                    for c in cursor.get_children():
                        if c.kind == clang.cindex.CursorKind.STRUCT_DECL:
                            struct_name = c.spelling
                            #if struct_name not in processed_structs:
                            typedef_struct_decl = f"typedef struct {c.spelling} {{\n"
                            for inner_field in c.get_children():
                                if inner_field.kind == clang.cindex.CursorKind.FIELD_DECL:
                                    typedef_struct_decl += f"    {inner_field.type.spelling} {inner_field.spelling};\n"
                            struct_decl = typedef_struct_decl[8:] + f"}};"
                            typedef_struct_decl += f"}} {cursor.spelling};"
                            if struct_decl in definitions:
                                index = definitions.index(struct_decl)
                                definitions[index] = [typedef_struct_decl, cursor_filename]
                            elif typedef_struct_decl not in definitions:
                                definitions.append([typedef_struct_decl, cursor_filename])
                                processed_structs.add(struct_name)
                        elif c.kind == clang.cindex.CursorKind.UNION_DECL:
                            union_name = c.spelling
                            if union_name not in processed_unions:
                                typedef_union_decl = f"typedef union {union_name} {{\n"
                                for inner_field in c.get_children():
                                    if inner_field.kind == clang.cindex.CursorKind.FIELD_DECL:
                                        typedef_union_decl += f"    {inner_field.type.spelling} {inner_field.spelling};\n"
                                union_decl = typedef_union_decl[8:] + f"}};"
                                typedef_union_decl += f"}} {cursor.spelling};"
                                if union_decl in definitions:
                                    index = definitions.index(union_decl)
                                    definitions[index] = [typedef_union_decl, cursor_filename]
                                elif typedef_union_decl not in definitions:
                                    definitions.append([typedef_union_decl, cursor_filename])
                                    processed_unions.add(union_name)
                        elif c.kind == clang.cindex.CursorKind.ENUM_DECL:
                            enum_name = c.spelling
                            if enum_name not in processed_enums:
                                typedef_enum_decl = f"typedef enum {enum_name} {{\n"
                                for inner_field in c.get_children():
                                    if inner_field.kind == clang.cindex.CursorKind.ENUM_CONSTANT_DECL:
                                        typedef_enum_decl += f"    {inner_field.spelling},\n"
                                enum_decl = typedef_enum_decl[8:] + f"}};"
                                typedef_enum_decl += f"}} {cursor.spelling};"
                                if enum_decl in definitions:
                                    index = definitions.index(enum_decl)
                                    definitions[index] = [enum_decl, cursor_filename]
                                if typedef_enum_decl not in definitions:
                                    definitions.append([typedef_enum_decl, cursor_filename])
                                    processed_enums.add(enum_name)
                        elif c.kind == clang.cindex.CursorKind.TYPE_REF:
                            underlying_type = cursor.underlying_typedef_type.spelling
                            typedef_statement = f"typedef {underlying_type} {typedef_name};"
                            if typedef_statement not in definitions:
                                definitions.append([typedef_statement, cursor_filename])
                        elif c.kind == clang.cindex.CursorKind.PARM_DECL:
                            start = cursor.extent.start
                            end = cursor.extent.end
                            full_code = header_full_code
                            if parsed_filename not in cursor.extent.start.file.name:
                                full_code = get_code_lines(cursor.extent.start.file.name)
                            typedef_lines = full_code[start.line-1:end.line]
                            typedef_lines = [line.rstrip() for line in typedef_lines]
                            if len(typedef_lines) == 1:
                                typedef_content = "\n".join(typedef_lines)[start.column-1:end.column]
                            else:
                                start_line = typedef_lines[0]
                                end_line = typedef_lines[-1]
                                start_code = start_line[start.column-1:]
                                end_code = end_line[:end.column]
                                typedef_lines[0] = start_code
                                typedef_lines[-1] = end_code
                                typedef_content = "\n".join(typedef_lines)
                            if typedef_content not in definitions:
                                definitions.append([typedef_content, cursor_filename])
        elif cursor.kind == clang.cindex.CursorKind.UNION_DECL:
            union_name = cursor.spelling
            if union_name not in processed_unions:
                union_decl = f"union {union_name} {{\n"
                for field in cursor.get_children():
                    if field.kind == clang.cindex.CursorKind.FIELD_DECL:
                        union_decl += f"    {field.type.spelling} {field.spelling};\n"
                union_decl += "};"
                if union_decl not in definitions:
                    definitions.append([union_decl, cursor_filename])
                    processed_unions.add(union_name)
        elif cursor.kind == clang.cindex.CursorKind.ENUM_DECL:
            enum_name = cursor.spelling or "anonymous_enum"
            if enum_name not in processed_enums:
                enum_decl = f"enum {enum_name} {{\n"
                for field in cursor.get_children():
                    if field.kind == clang.cindex.CursorKind.ENUM_CONSTANT_DECL:
                        enum_decl += f"    {field.spelling},\n"
                enum_decl += "};"
                if enum_decl not in definitions and enum_name != "anonymous_enum":
                    definitions.append([enum_decl, cursor_filename])
                    processed_enums.add(enum_name)
    
    return definitions

# if __name__ == "__main__":
#     parser = argparse.ArgumentParser(description="Parse .h file and extract global symbol content.")
#     parser.add_argument('--header_file', type=str, help="Path to the hearder file (.h)")
#     #parser.add_argument('--include_path', type=str, help="inclusion path")

#     args = parser.parse_args()
#     header_file = args.header_file
#     from utils import system_header_paths
#     extract_definitions_from_header(header_file, system_header_paths)


def test_header_extract():
    import sys
    
    # 设置测试文件路径
    test_header_file = "test_files/include/test.h"
    
    # 模拟命令行参数
    sys.argv = [
        "c_header_extract.py",
        "--header_file",
        test_header_file
    ]
    
    # 调用原main函数的逻辑
    parser = argparse.ArgumentParser(description="Parse .h file and extract global symbol content.")
    parser.add_argument('--header_file', type=str, help="Path to the header file (.h)")
    
    args = parser.parse_args()
    header_file = args.header_file
    from utils import system_header_paths
    extract_definitions_from_header(header_file, system_header_paths)

if __name__ == "__main__":
    test_header_extract()
